﻿using CrowdMonitoring;
using OfficeOpenXml;
using OpenCvSharp;
using OpenCvSharp.Face;
using OpenCvSharp.Internal.Vectors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading;

namespace FaceRecognitionAgainstTrainData
{
    /// <summary>
    /// 人脸比对工具（单张人脸 vs Python训练数据）
    /// </summary>
    public class FaceTrainDataComparer
    {
        #region 配置参数（与Python端完全对齐）
        // 人脸检测器配置（同Python的detectMultiScale参数）
        private const string HaarCascadePath = "haarcascade_frontalface_default.xml";
        private const int MinFaceSize = 100; // 最小人脸尺寸
        private const double ScaleFactor = 1.2; // 缩放因子
        private const int MinNeighbors = 3; // 最小邻域数

        // 训练数据与模型配置
        private const string LbphModelPath = "trainner.yml"; // Python生成的LBPH模型
        private const string UserConfigPath = "config.txt"; // Python生成的用户ID-名称映射
        private const double ConfidenceThreshold = 96.0; // 匹配阈值（≤85为有效匹配）

        // 人脸预处理配置（同Python的预处理逻辑）
        private const int ResizedFaceWidth = 100; // 统一人脸宽度
        private const int ResizedFaceHeight = 100; // 统一人脸高度
        #endregion

        #region 核心对象与数据
        private readonly CascadeClassifier _faceDetector; // 人脸检测器
        private readonly LBPHFaceRecognizer _lbphRecognizer; // LBPH识别器（加载Python模型）
        private readonly Dictionary<int, string> _userIdToNameMap; // 用户ID→用户名映射

        // 新增：公共属性，用于获取已注册用户数量
        public int RegisteredUserCount => _userIdToNameMap.Count;
        #endregion


        // 人脸检测分类器
        private static readonly CascadeClassifier _faceCascade = new CascadeClassifier("haarcascade_frontalface_default.xml");

        /// <summary>
        /// 初始化人脸比对工具（加载模型和用户配置）
        /// </summary>
        /// <exception cref="FileNotFoundException">必要文件缺失时抛出</exception>
        public FaceTrainDataComparer()
        {
            // 1. 加载人脸检测器
            if (!File.Exists(HaarCascadePath))
                throw new FileNotFoundException("人脸检测器文件缺失", HaarCascadePath);
            _faceDetector = new CascadeClassifier(HaarCascadePath);

            // 2. 加载Python训练的LBPH模型
            if (!File.Exists(LbphModelPath))
                throw new FileNotFoundException("Python人脸训练模型缺失", LbphModelPath);
            _lbphRecognizer = LBPHFaceRecognizer.Create();
            _lbphRecognizer.Read(LbphModelPath); // 读取Python的trainner.yml

            // 3. 加载用户配置（从Python的config.txt读取ID-用户名映射）
            _userIdToNameMap = LoadUserConfig();
            if (_userIdToNameMap.Count == 0)
                throw new Exception("用户配置文件config.txt为空或格式错误");
        }

        #region 核心功能：单张人脸 vs 训练数据比对
        /// <summary>
        /// 比对单张人脸照片与训练数据，返回最匹配的用户信息
        /// </summary>
        public FaceTrainDataResult CompareWithTrainData(string faceImagePath)
        {
            // 1. 验证输入文件
            if (!File.Exists(faceImagePath))
                throw new FileNotFoundException("待比对的人脸照片不存在", faceImagePath);

            // 2. 从照片中提取人脸并预处理
            using Mat preprocessedFace = ExtractAndPreprocessFace(faceImagePath);
            if (preprocessedFace.Empty())
                throw new Exception("待比对照片中未检测到人脸");

            // 3. 用LBPH模型预测（核心：与训练数据中的用户比对）
            _lbphRecognizer.Predict(preprocessedFace, out int predictedUserId, out double confidence);

            // 4. 解析预测结果
            bool isMatched = confidence < ConfidenceThreshold && _userIdToNameMap.ContainsKey(predictedUserId);
            string userName = isMatched ? _userIdToNameMap[predictedUserId] : "unknown";
            string matchDesc = isMatched
                ? $"匹配到已注册用户（置信度{confidence:F2}，≤96为有效匹配）"
                : $"未匹配到已注册用户（置信度{confidence:F2}，超过阈值{ConfidenceThreshold}）";

            // 5. 封装结果
            return new FaceTrainDataResult
            {
                IsMatchedWithTrainData = isMatched,
                PredictedUserId = predictedUserId,
                PredictedUserName = userName,
                Confidence = Math.Round(confidence, 2),
                ConfidenceThreshold = ConfidenceThreshold,
                MatchDescription = matchDesc,
                TotalRegisteredUsers = _userIdToNameMap.Count,
                InputImagePath = faceImagePath
            };
        }
        #endregion

        #region 辅助方法1：加载Python的config.txt用户配置
        private Dictionary<int, string> LoadUserConfig()
        {
            var userMap = new Dictionary<int, string>();
            if (!File.Exists(UserConfigPath))
                throw new FileNotFoundException("用户配置文件缺失", UserConfigPath);

            // 读取文件内容（格式：每行"用户ID 用户名"）
            string[] lines = File.ReadAllLines(UserConfigPath, System.Text.Encoding.UTF8);
            foreach (string line in lines)
            {
                string trimmedLine = line.Trim();
                if (string.IsNullOrEmpty(trimmedLine))
                    continue;

                string[] parts = trimmedLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length != 2 || !int.TryParse(parts[0], out int userId))
                {
                    Console.WriteLine($"警告：无效的用户配置行：{trimmedLine}（跳过）");
                    continue;
                }

                // 避免重复ID
                if (userMap.ContainsKey(userId))
                    userMap[userId] = parts[1];
                else
                    userMap.Add(userId, parts[1]);
            }

            return userMap;
        }
        #endregion

        #region 辅助方法2：人脸提取与预处理
        private Mat ExtractAndPreprocessFace(string imagePath)
        {
            using Mat originalImage = Cv2.ImRead(imagePath);
            if (originalImage.Empty())
                return new Mat(); // 图片读取失败

            // 1. 转为灰度图
            using Mat grayImage = new Mat();
            Cv2.CvtColor(originalImage, grayImage, ColorConversionCodes.BGR2GRAY);

            // 2. 直方图均衡化
            Cv2.EqualizeHist(grayImage, grayImage);

            // 3. 检测人脸
            Rect[] faceRects = _faceDetector.DetectMultiScale(
                image: grayImage,
                scaleFactor: ScaleFactor,
                minNeighbors: MinNeighbors,
                flags: HaarDetectionTypes.ScaleImage,
                minSize: new Size(MinFaceSize, MinFaceSize)
            );

            // 4. 提取第一个人脸并统一尺寸
            if (faceRects.Length == 0)
                return new Mat(); // 未检测到人脸

            Rect faceRect = faceRects[0];
            using Mat extractedFace = new Mat(grayImage, faceRect);
            Mat resizedFace = new Mat();
            Cv2.Resize(extractedFace, resizedFace, new Size(ResizedFaceWidth, ResizedFaceHeight));

            return resizedFace;
        }
        #endregion
        #region 辅助方法2：人脸提取与预处理
        /// <summary>
        /// 使用OpenCvSharp将YUV420p文件转换为PNG图像
        /// </summary>
        /// <param name="yuvFilePath">YUV文件路径</param>
        /// <param name="outputPngPath">输出PNG路径</param>
        /// <param name="width">图像宽度</param>
        /// <param name="height">图像高度</param>
        /// <param name="errorMessage">错误信息</param>
        /// <returns>转换是否成功</returns>
        internal bool ConvertYuv420pToPng(
            string yuvFilePath,
            string outputPngPath,
            int width,
            int height,
            out string errorMessage)
        {
            errorMessage = string.Empty;

            try
            {
                // 验证输入文件
                if (!File.Exists(yuvFilePath))
                {
                    errorMessage = $"YUV文件不存在: {yuvFilePath}";
                    return false;
                }

                // 计算YUV420p文件的预期大小 (width * height * 1.5)
                long expectedSize = (long)width * height * 3 / 2;
                var fileInfo = new FileInfo(yuvFilePath);

                if (fileInfo.Length != expectedSize)
                {
                    errorMessage = $"YUV文件大小不匹配。预期: {expectedSize} 字节，实际: {fileInfo.Length} 字节";
                    return false;
                }

                // 读取YUV文件数据
                byte[] yuvData = File.ReadAllBytes(yuvFilePath);

                // 分离Y、U、V分量
                int ySize = width * height;
                int uvSize = width * height / 4;

                // 创建Y分量矩阵 (单通道)
                using var yMat = new Mat(height, width, MatType.CV_8UC1);
                Marshal.Copy(yuvData, 0, yMat.Data, ySize);

                // 创建U分量矩阵 (单通道，1/2分辨率)
                using var uMat = new Mat(height / 2, width / 2, MatType.CV_8UC1);
                Marshal.Copy(yuvData, ySize, uMat.Data, uvSize);

                // 创建V分量矩阵 (单通道，1/2分辨率)
                using var vMat = new Mat(height / 2, width / 2, MatType.CV_8UC1);
                Marshal.Copy(yuvData, ySize + uvSize, vMat.Data, uvSize);

                // 对U和V分量进行上采样到原始分辨率
                using var uResized = new Mat();
                using var vResized = new Mat();
                Cv2.Resize(uMat, uResized, new Size(width, height), 0, 0, InterpolationFlags.Linear);
                Cv2.Resize(vMat, vResized, new Size(width, height), 0, 0, InterpolationFlags.Linear);

                // 合并Y、U、V通道为YUV矩阵
                using var yuvMat = new Mat();
                Cv2.Merge(new[] { yMat, uResized, vResized }, yuvMat);

                // 转换YUV到BGR（OpenCV默认存储格式）
                using var bgrMat = new Mat();
                Cv2.CvtColor(yuvMat, bgrMat, ColorConversionCodes.YUV2BGR);

                // 保存为PNG
                bool saveSuccess = Cv2.ImWrite(outputPngPath, bgrMat);
                if (!saveSuccess)
                {
                    errorMessage = $"无法保存PNG文件到: {outputPngPath}";
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                errorMessage = $"转换失败: {ex.Message}";
                return false;
            }
        }

        #endregion
    }

    /// <summary>
    /// 单张人脸与训练数据的比对结果实体类
    /// </summary>
    public class FaceTrainDataResult
    {
        /// <summary>输入的图片文件路径</summary>
        public string InputImagePath { get; set; }

        /// <summary>是否匹配到训练数据中的已注册用户</summary>
        public bool IsMatchedWithTrainData { get; set; }

        /// <summary>预测的用户ID（对应Python端的User.ID）</summary>
        public int PredictedUserId { get; set; }

        /// <summary>预测的用户名（从config.txt读取，未匹配则为unknown）</summary>
        public string PredictedUserName { get; set; }

        /// <summary>置信度（越小越匹配，范围0~100+）</summary>
        public double Confidence { get; set; }

        /// <summary>匹配阈值（≤此值为有效匹配）</summary>
        public double ConfidenceThreshold { get; set; }

        /// <summary>匹配结果描述</summary>
        public string MatchDescription { get; set; }

        /// <summary>训练数据中已注册的用户总数</summary>
        public int TotalRegisteredUsers { get; set; }

        /// <summary>重写ToString，便于打印详细结果</summary>
        public override string ToString()
        {
            return $"===== 人脸与训练数据比对结果 =====\n" +
                   $"输入图片：{InputImagePath}\n" +
                   $"已注册用户总数：{TotalRegisteredUsers}\n" +
                   $"是否匹配已注册用户：{IsMatchedWithTrainData}\n" +
                   $"预测用户ID：{PredictedUserId}\n" +
                   $"预测用户名：{PredictedUserName}\n" +
                   $"置信度：{Confidence}（阈值：{ConfidenceThreshold}）\n" +
                   $"结果描述：{MatchDescription}";
        }
    }

    /// <summary>
    /// 用于JSON输出的结果格式
    /// </summary>
    public class RecognitionReport
    {
        public string InputImage { get; set; }
        public string UserName { get; set; }
    }
    // 消息队列辅助类
    public class MessageQueue
    {
        public void SendToApi(object data)
        {
            //Console.WriteLine($"发送数据到API: {Newtonsoft.Json.JsonConvert.SerializeObject(data)}");
            // 实际项目中实现API调用逻辑
        }
    }

    #region 程序入口（命令行版本）
    class Program
    {
        // 全局变量
        private static bool _isRunning = false;
        private static readonly object _lock = new object();
        private static string _apiResult;
        private static MessageQueue _messageQueue;
        private static Dictionary<int, string> _userDict;
        private static int _beginRow = 1;
        private static ExcelWorksheet _ws;
        private static ExcelPackage _wb;
        private static readonly string _today = DateTime.Now.ToString("yyyyMMdd");
        // 人脸检测分类器
        private static readonly CascadeClassifier _faceCascade = new CascadeClassifier("haarcascade_frontalface_default.xml");

        private static void ScanFace()
        {
            // 检查训练模型是否存在
            if (!File.Exists("trainner.yml"))
            {
                UpdateTip("请先注册人脸!");
                AckApiScanFailed();
                return;
            }

            // 启动识别线程
            var recognitionThread = new Thread(RunRecognition)
            {
                Name = "scan_face",
                IsBackground = true
            };
            recognitionThread.Start();
        }

        private static void RunRecognition()
        {
            Console.WriteLine($"当前运行线程：'{Thread.CurrentThread.Name}'");

            lock (_lock)
            {
                try
                {
                    // 打开摄像头
                    using var capture = new VideoCapture(0);
                    if (!capture.IsOpened())
                    {
                        UpdateTip("摄像头打开异常!");
                        AckApiScanFailed();
                        return;
                    }

                    // 初始化识别参数
                    var startTime = DateTime.Now;
                    const int timeout = 10;
                    bool hasRecognized = false;
                    DateTime lastFaceTime = DateTime.MinValue;
                    Mat savedFrame = null;

                    // 加载训练模型
                    UpdateTip("正在识别...");
                    var recognizer = LBPHFaceRecognizer.Create();
                    try
                    {
                        recognizer.Read("trainner.yml");
                    }
                    catch (Exception ex)
                    {
                        UpdateTip($"模型 trainner.yml 未正确加载! 错误：{ex.Message}");
                        AckApiScanFailed();
                        return;
                    }

                    // 循环识别
                    while (!hasRecognized)
                    {
                        // 检查退出信号
                        if (Console.KeyAvailable)
                        {
                            var key = Console.ReadKey(true);
                            if (key.Key == ConsoleKey.Q)
                            {
                                _isRunning = false;
                                AckApiScanFailed();
                                return;
                            }
                        }

                        // 读取摄像头帧
                        var frame = new Mat();
                        bool ret = capture.Read(frame);
                        if (!ret)
                        {
                            UpdateTip("摄像头读取失败!");
                            break;
                        }

                        // 转换为灰度图像
                        var grayFrame = new Mat();
                        Cv2.CvtColor(frame, grayFrame, ColorConversionCodes.BGR2GRAY);

                        // 检测人脸
                        Rect[] faces = _faceCascade.DetectMultiScale(
                            grayFrame,
                            1.2,
                            3,
                            HaarDetectionTypes.ScaleImage,
                            new Size(100, 100)
                        );

                        // 处理检测到的人脸
                        if (faces.Length > 0)
                        {
                            foreach (var faceRect in faces)
                            {
                                // 截取人脸区域
                                var faceRoi = new Mat(grayFrame, faceRect);

                                // 预测人脸
                                int predictedId = -1;
                                double confidence = 0;
                                try
                                {
                                    recognizer.Predict(faceRoi, out predictedId, out confidence);
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"预测错误: {ex.Message}");
                                    continue;
                                }

                                // 验证结果
                                string userName = "unknown";
                                if (confidence < 85 && _userDict.ContainsKey(predictedId))
                                {
                                    userName = _userDict[predictedId];
                                    hasRecognized = true;

                                    // 触发识别成功逻辑
                                    UpdateTip($"识别成功: {userName}");
                                    _apiResult = userName;

                                    // 发送结果到API
                                    _messageQueue.SendToApi(new
                                    {
                                        type = "scan_result",
                                        data = userName
                                    });

                                    // 写入结果到txt文件
                                    File.WriteAllText("api_result.txt", userName, System.Text.Encoding.UTF8);

                                    // 更新Excel考勤表
                                    UpdateAttendanceExcel(userName);
                                }

                                // 在帧上绘制人脸框和用户名
                                Cv2.Rectangle(frame, faceRect, new Scalar(0, 255, 0), 2);
                                Cv2.PutText(
                                    frame,
                                    userName,
                                    new Point(faceRect.X + 5, faceRect.Y - 10),
                                    HersheyFonts.HersheySimplex,
                                    0.6,
                                    new Scalar(0, 0, 255),
                                    2
                                );
                            }

                            // 保存人脸帧
                            savedFrame = new Mat();
                            Cv2.CvtColor(frame, savedFrame, ColorConversionCodes.BGR2RGB);
                            lastFaceTime = DateTime.Now;
                        }

                        // 检查超时
                        if ((DateTime.Now - startTime).TotalSeconds > timeout && !hasRecognized)
                        {
                            UpdateTip("识别超时!");
                            AckApiScanFailed();
                            return;
                        }

                        // 释放临时帧资源
                        frame.Release();
                        grayFrame.Release();
                    }
                }
                finally
                {
                    Console.WriteLine($"线程 '{Thread.CurrentThread.Name}' 结束");
                    AckApiScanFailed();
                }
            }
        }

        // 辅助方法
        private static void UpdateTip(string message)
        {
            Console.WriteLine($"提示: {message}");
        }

        private static void AckApiScanFailed()
        {
            Console.WriteLine("扫描完成或失败，通知API");
        }

        private static void UpdateAttendanceExcel(string userName)
        {
            try
            {
                string excelPath = $"考勤表({_today}).xlsx";

                // 初始化Excel
                if (_wb == null)
                {
                    if (File.Exists(excelPath))
                    {
                        _wb = new ExcelPackage(new FileInfo(excelPath));
                    }
                    else
                    {
                        _wb = new ExcelPackage();
                        _ws = _wb.Workbook.Worksheets.Add("考勤记录");
                        _ws.Cells[1, 1].Value = "用户名";
                        _ws.Cells[1, 2].Value = "考勤时间";
                        _beginRow = 2;
                    }
                    _ws = _wb.Workbook.Worksheets[0];
                }

                // 写入考勤数据
                if (_beginRow == 1 && _ws.Cells[_beginRow, 1].Value == null)
                {
                    _ws.Cells[_beginRow, 1].Value = userName;
                    _ws.Cells[_beginRow, 2].Value = DateTime.Now.ToString("HH:mm:ss");
                }
                else if (_beginRow == 1 || _ws.Cells[_beginRow, 1].Value?.ToString() != userName)
                {
                    _beginRow++;
                    _ws.Cells[_beginRow, 1].Value = userName;
                    _ws.Cells[_beginRow, 2].Value = DateTime.Now.ToString("HH:mm:ss");
                }

                // 保存Excel
                _wb.SaveAs(new FileInfo(excelPath));

                Console.WriteLine("考勤记录已更新");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"更新考勤表失败: {ex.Message}");
            }
        }
        static void crowdmon()
        {
            // 1. 初始化后台检测器（需确保haarcascade_frontalface_default.xml在输出目录）
            using (var monitor = new CrowdAndLeaveMonitor(
                cameraIndex: 0, // 摄像头索引（0为默认摄像头）
                haarCascadePath: @"c:\pgina\haarcascade_frontalface_default.xml" // 正脸检测器路径
            ))
            {
                // 2. 注册围观状态回调（处理围观报警）
                monitor.CrowdStatusChanged += (personCount, isCrowded) =>
                {
                    if (isCrowded)
                    {
                        // 此处可添加围观报警逻辑：播放声音、发送邮件等
                        Console.WriteLine("=== 触发围观报警 ===");
                    }
                };

                // 3. 注册人员离开状态回调（可选，处理离开通知）
                monitor.LeaveStatusChanged += (hasPerson, noPersonSec, hasLocked) =>
                {
                    if (hasLocked)
                    {
                        // 系统锁定后可选择停止监控（或继续运行，等待人员返回）
                        // monitor.StopMonitoring();
                    }
                };

                // 4. 注册错误回调
                monitor.ErrorOccurred += (errorMsg) =>
                {
                    if (!string.IsNullOrEmpty(errorMsg))
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine($"【错误】{errorMsg}");
                        Console.ResetColor();
                    }
                };

                // 3. 开始监控
                monitor.StartMonitoring();

                // 4. 等待用户输入，保持程序运行（控制台程序必需）
                Console.WriteLine("后台监控已启动，按任意键停止...");
                Console.ReadKey();

                // 5. 停止监控（using语句会自动调用Dispose，此处可省略，但显式调用更清晰）
                monitor.StopMonitoring();
            }

            // 6. 程序结束
            Console.WriteLine("程序已退出");
            Console.ReadKey();
        }
        static void Main(string[] args)
        {
            try
            {
                // 检查命令行参数
                if (args.Length == 0)
                {
                    crowdmon();
                    Console.WriteLine("请提供待比对的人脸照片路径作为命令行参数。");
                    Console.WriteLine("用法：FaceRecognitionAgainstTrainData没有命令参数表示进行人脸动态识别，");
                    Console.WriteLine("用法：FaceRecognitionAgainstTrainData <图片路径>");
                    Console.WriteLine("示例：FaceRecognitionAgainstTrainData snapshot.jpg");
                    Console.WriteLine("人脸识别系统启动...");
                    //Console.WriteLine("按 's' 开始识别，按 'q' 退出程序");

                    // 初始化用户字典
                    _userDict = new Dictionary<int, string>
                    {
                        {0, "张三"},
                        {1, "李四"},
                        {2, "王五"}
                    };

                    // 初始化消息队列
                    _messageQueue = new MessageQueue();

                    // 处理用户输入
                    //while (true)
                    {
                        //var key = Console.ReadKey(true);
                        //if (key.Key == ConsoleKey.S)
                        {
                            Console.WriteLine("开始人脸扫描...");
                            ScanFace();
                        }
                        //else if (key.Key == ConsoleKey.Q)
                        {
                        //    Console.WriteLine("程序退出");
                        //    break;
                        }
                    }

                    // 释放资源
                    _faceCascade?.Dispose();
                    _wb?.Dispose();
                }
                else
                {
                    string facePath = args[0];

                    // 1. 初始化人脸比对工具
                    Console.WriteLine("正在初始化人脸比对工具...");
                    var faceComparer = new FaceTrainDataComparer();
                    Console.WriteLine($"初始化完成！已加载 {faceComparer.RegisteredUserCount} 个已注册用户的训练数据\n");
                    FaceTrainDataResult result;
                    // 2.-1. 转一下文件
                    if (facePath.Contains(".yuv"))
                    {
                        string outputPng = @"C:\programdata\coconet\output.png";
                        int width = 1280;
                        int height = 720;

                        if (faceComparer.ConvertYuv420pToPng(facePath, outputPng, width, height, out string error))
                        {
                            Console.WriteLine($"成功转换: {outputPng}");
                        }
                        else
                        {
                            Console.WriteLine($"转换失败: {error}");
                        }
                        // 2. 执行比对
                        Console.WriteLine($"正在比对人脸与训练数据...");
                        result = faceComparer.CompareWithTrainData(outputPng);
                        // 3. 输出比对结果到控制台
                        Console.WriteLine("\n" + result.ToString());
                    }
                    else
                    {
                        // 2. 执行比对
                        Console.WriteLine($"正在比对人脸与训练数据...");
                        result = faceComparer.CompareWithTrainData(facePath);
                        // 3. 输出比对结果到控制台
                        Console.WriteLine("\n" + result.ToString());
                    }

                    // 4. 生成并保存JSON报告
                    var report = new RecognitionReport
                    {
                        InputImage = facePath,
                        UserName = result.PredictedUserName
                    };

                    string json = JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true });
                    File.WriteAllText("recgreport.json", json);
                    Console.WriteLine("\n识别结果已保存到 recgreport.json");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\n错误：{ex.Message}");
            }
            finally
            {
               // Console.WriteLine("\n按任意键退出...");
               // Console.ReadKey();
            }
        }
    }
    #endregion
}
